/******************************************************************************
 * $Id: file.c,v 1.44 2004/09/22 01:14:22 jimb Exp $
 * @file
 * @author	Jim Brooks http://www.jimbrooks.org
 * @brief	Functions for files.
 ******************************************************************************/

#include "common.h"
#include "file.h"
#include "selector.h"
#include "popup.h"

/******************************************************************************
 * Load a regular file into a memory buffer.
 * For special files, instead use LoadFileSpecial().
 *
 * The buffer will be appended with a null char, but will not affect
 * the returned file size.
 *
 * The size of the file must be > 0.
 * The size of the file is not expected to change during this function.
 * If it does, this function will interpret a file size change as an error.
 *
 * @param	pFileName
 * @param	pFileSize (OUT)
 *          Excludes null char that will be appended to buffer.
 * @return	Pointer to memory buffer allocated by ** g_malloc() **
 *          -or- NULL upon failure.
 *          The size of the buffer is 1 char greater than file size.
 ******************************************************************************/
char*
LoadFile( gchar* pFileName,
          guint* pFileSize /* OUT */ )
{
   FILE*	pFile	= NULL;
   char*	pBuffer	= NULL;
   size_t	bytes	= 0;
   long     sizel	= 0;

  /*
   * Determine the size of the file.
   * Cause an error if the size is not > 0.
   */
   *pFileSize = 0;

   sizel = FileSize( pFileName );
   if ( sizel < 1 ) return NULL;

   *pFileSize = (guint) sizel;

  /*
   * Allocate the memory buffer according to the size of the file.
   * Include an extra byte for a null char to append.
   */
   pBuffer = (char*) g_malloc( *pFileSize + 10 ); /* extra to be safe */

  /*
   * Open the file for reading.
   */
   pFile = fopen( pFileName, "r" );

  /*
   * Read the file into the memory buffer.
   */
   bytes = \
   fread( pBuffer,
          1,
          *pFileSize,
          pFile );

  /*
   * Close the file.
   */
   fclose( pFile );

  /*
   * Append a null char.
   */
   pBuffer[*pFileSize] = '\0';
#if 0
/********==------lv added for file encoding----=--------****/
   const gchar * FileCharset = detect_charset(pBuffer);
   
   /* 如果 pBuffer 中字符串的字符集不是 UTF-8 编码 */
   if ( !g_utf8_validate( pBuffer , -1, NULL) )
   {
   	GError *error;
   	size_t newReadSize, newWriteSize;
   	char * pNewBuffer = NULL;
   	pNewBuffer = g_convert(pBuffer,
   							*pFileSize,
							"UTF-8",
							FileCharset,
   							&newReadSize,
   							&newWriteSize,
   							&error);
   	if ( NULL != pNewBuffer && *pFileSize == newReadSize )
   	{
   		g_free(pBuffer);
   		pBuffer = pNewBuffer;
   		*pFileSize = newWriteSize;
   		 bytes = newWriteSize;
   	}
        else
        {
        	g_print("g_convert from %s to %s error.\n",pModuleLoaded->pCharset, "UTF-8");
        	g_print("Error code:%s\n", error->message);
        	g_error_free(error);
        	g_free(pNewBuffer);
        }
   }
/******----------------------------------------------------*********/
#endif
  /*
   * Return a pointer to the buffer if successful.
   */
   if ( (bytes == *pFileSize) && bytes ) 
	   return pBuffer;

  /*
   * An error occurred, free the buffer, return NULL.
   */
   g_free( pBuffer );
   Popup( _("Failed to load file"), _("Error"), FONT_PROPORTIONAL );
   return NULL;
}

#if 0 //此函数未真正使用

/******************************************************************************
 * Load a special file (whose size can't be predetermined) into a GString.
 *
 * @param	pFileName
 * @return	Pointer to GString or NULL if failure.
 *          Note that zero-length is not considered a failure here.
 ******************************************************************************/
GString*
LoadFileSpecial( gchar* pFileName )
{
   FILE*	pFile	= NULL;
   char		bufChar[1];
   GString* pGstr;

  /*
   * Create GString, open the file for reading.
   */
   pGstr = g_string_sized_new( 0 );
   if ( pGstr == NULL ) return NULL;

   pFile = fopen( pFileName, "r" );
   if ( pFile == NULL ) return NULL;

  /*
   * Read the file, one char at a time, into a GString, until EOF.
   */
   for (;;)
   {
      int cnt;

     /*
      * Read next char.
      */
      cnt = \
      fread( &bufChar,
             1,
             1,
             pFile );

     /*
      * If present, append next char into GString, else break.
      */
      if ( cnt == 1 )
         pGstr = g_string_append_c( pGstr, bufChar[0] );
      else
         break;
   }

  /*
   * Close the file.
   */
   fclose( pFile );

  /*
   * Return GString.
   */
   return pGstr;
}

#endif

/******************************************************************************
 * @return The size of a file (returns negative upon failure).
 ******************************************************************************/
long
FileSize( gchar* pFilename )
{
   FILE*	file	= NULL;
   long		fsize	= -1;

  /*
   * Try to open the file for reading.
   */
   file = fopen( pFilename, "r" );

  /*
   * Ensure the file was opened.
   */
   if ( file == NULL ) return FUNC_FAIL_NEGATIVE;

  /*
   * Use fseek() and ftell() to determine the file size.
   *
   * fseek() returns 0 upon success.
   * ftell() returns the current offset (ie the file size in this case).
   *
   */
   if ( fseek( file, 0, SEEK_END ) ) { fclose( file ); return FUNC_FAIL_NEGATIVE; }
   fsize = ftell( file );

  /*
   * Close the file and return its size.
   */
   fclose( file );
   return fsize;
}

/*****************************************************************************
 * @return True if a file suffix matches (case-INSENSITIVE).
 *****************************************************************************/
int
FileSuffixMatch( char* pFilename,
                 char* pSuffix )
{
   int	lenFilename;
   int	lenSuffix;

   if ( ! pFilename ) return FALSE;

  /*
   * Ignore case in case of MSDOG filenames.
   */
   lenFilename = strlen( pFilename );
   lenSuffix   = strlen( pSuffix );

   if ( lenFilename <= lenSuffix )
      return FALSE;

   return ! CompareStringsNonCase( pFilename + lenFilename - lenSuffix, pSuffix );
}

/*****************************************************************************
 * @return True if a file's extension matches any --langmap cmd-line arg.
 *****************************************************************************/
int
LangmapMatch( char* pFilename,
              char* pLang ) /* include trailing colon (eg "perl:") for precise match */
{
   int   i;
   char* pSuffix;

   if ( ! pFilename ) return FALSE;

  /*
   * Find file suffix.
   */
   pSuffix = strrchr( pFilename, '.' );			/* find last '.' */
   if ( pSuffix == NULL ) return FALSE;			/* unless file has no suffix */

  /*
   * For every -langmap arg that was passed.
   */
   for ( i = 0; i < langmapCnt; ++i )
   {
     /*
      * If this --langmap correlates to language caller specified.
      */
      if ( strstr( langmap[i], pLang ) != NULL )
      {
        /*
         * Return true if file suffix is contained in the -langmap arg.
         */
         return strstr( langmap[i], pSuffix ) != NULL;  /* compare including '.' */
      }
   }

   return FALSE; /* -langmap wasn't passed or no match */
}

/*****************************************************************************
 * @return One of SRC_FILE_* based on filename extension.
 *****************************************************************************/
int
WhichKindOfSrcFile( char* pFilename )
{
   if      ( IsSrcFileC( pFilename ) )			/* C */
      return SRC_FILE_C;
   else if ( IsSrcFileCPlusPlus( pFilename ) )	/* C++ */
      return SRC_FILE_CPLUSPLUS;
   else if ( IsSrcFileJava( pFilename ) )		/* Java */
      return SRC_FILE_JAVA;
   else if ( IsSrcFileAsm( pFilename ) )		/* assembly */
      return SRC_FILE_ASM;
   else if ( IsSrcFilePerl( pFilename ) )		/* Perl */
      return SRC_FILE_PERL;
   else if ( IsSrcFilePython( pFilename ) )		/* Python */
      return SRC_FILE_PYTHON;
   else if ( IsSrcFileForth( pFilename ) )		/* FORTH */
      return SRC_FILE_FORTH;
   else
      return SRC_FILE_OTHER;
}

/*****************************************************************************
 * @return True if a file appears to be C/C++/Java source file.
 *****************************************************************************/
int
IsSrcFileCish( char* pFilename )
{
   return    IsSrcFileC(         pFilename )
          || IsSrcFileCPlusPlus( pFilename )
          || IsSrcFileJava(      pFilename );
}

/*****************************************************************************
 * @return True if a file appears to be a C source file.
 *****************************************************************************/
int
IsSrcFileC( char* pFilename )
{
   return    FileSuffixMatch( pFilename, ".c"   )
          || FileSuffixMatch( pFilename, ".h"   )
          || LangmapMatch(    pFilename, "c:"   );
}

/*****************************************************************************
 * @return True if a file appears to be a C++ source file.
 *****************************************************************************/
int
IsSrcFileCPlusPlus( char* pFilename )
{
   return    FileSuffixMatch( pFilename, ".cc"  )
          || FileSuffixMatch( pFilename, ".cpp" )
          || FileSuffixMatch( pFilename, ".cxx" )
          || FileSuffixMatch( pFilename, ".c++" )
#if 0
          || FileSuffixMatch( pFilename, ".h"   )  /* overlaps with C */
#endif
          || FileSuffixMatch( pFilename, ".hh"  )
          || FileSuffixMatch( pFilename, ".hpp" )
          || FileSuffixMatch( pFilename, ".hxx" )
          || FileSuffixMatch( pFilename, ".h++" )
          || FileSuffixMatch( pFilename, ".inl" )
          || LangmapMatch(    pFilename, "c++:" );
}

/*****************************************************************************
 * @return True if a file appears to be a Java source file.
 *****************************************************************************/
int
IsSrcFileJava( char* pFilename )
{
   return    FileSuffixMatch( pFilename, ".java" )
          || FileSuffixMatch( pFilename, ".jav"  )
          || FileSuffixMatch( pFilename, ".j"    )
          || LangmapMatch(    pFilename, "java:" );
}

/*****************************************************************************
 * @return True if a file appears to be an assembly source file.
 *****************************************************************************/
int
IsSrcFileAsm( char* pFilename )
{
   return    FileSuffixMatch( pFilename, ".asm" )
          || FileSuffixMatch( pFilename, ".inc" )
          || FileSuffixMatch( pFilename, ".s"   )
          || FileSuffixMatch( pFilename, ".sm4" )
          || LangmapMatch(    pFilename, "asm:" );
}

/*****************************************************************************
 * @return True if a file appears to be a Perl source file.
 *****************************************************************************/
int
IsSrcFilePerl( char* pFilename )
{
   return    FileSuffixMatch( pFilename, ".pl"  )
          || FileSuffixMatch( pFilename, ".pm"  )
          || FileSuffixMatch( pFilename, ".ph"  )
          || LangmapMatch(    pFilename, "perl:");
}

/*****************************************************************************
 * @return True if a file appears to be a Python source file.
 *****************************************************************************/
int
IsSrcFilePython( char* pFilename )
{
   return    FileSuffixMatch( pFilename, ".py"  )
          || LangmapMatch(    pFilename, "python:");
}

/*****************************************************************************
 * @return True if a file appears to be a FORTH source file.
 *****************************************************************************/
int
IsSrcFileForth( char* pFilename )
{
   return    FileSuffixMatch( pFilename, ".4th"  )
          || FileSuffixMatch( pFilename, ".fth"  )
          || FileSuffixMatch( pFilename, ".seq"  )
          || FileSuffixMatch( pFilename, ".fs"   ) // Gforth
//        || FileSuffixMatch( pFilename, ".fb"   ) // hypersrc can't show FORTH block files properly
          || LangmapMatch(    pFilename, "forth:");
}

/*****************************************************************************
 * Filter (remove) MSDOS CR chars from a buffer.
 * The output is a new buffer without CR chars.
 * The old buffer is left alone.
 *
 * @param	pBufOld
 * @param	sizeOld
 * @param	ppBufNew (OUT)
 * @param	pSizeNew (OUT)
 * @return	FUNC_OK if successful.
 *****************************************************************************/
FUNC_X
FilterMsdosCr( char*  pBufOld,
               int    sizeOld,
               char** ppBufNew, /*OUT*/
               int*   pSizeNew  /*OUT*/ )
{
   int		i, j;
   char*	pBufNew;
   int		sizeNew;

   *ppBufNew = NULL;
   *pSizeNew = 0;

  /*
   * Compute size of new buffer (size of old buffer minus CR chars).
   */
   sizeNew = sizeOld;
   for ( i = 0; i < sizeOld; i++ )
   {
      sizeNew -= (pBufOld[i] == CHAR_CR);
   }

  /*
   * Allocate a new buffer.
   */
   pBufNew = (char*) g_malloc( sizeNew + 10 );	/* extra for null char */
   if ( ! pBufNew ) return FUNC_FAIL;

  /*
   * Copy all non-CR chars.
   */
   for ( i = j = 0; i < sizeOld; i++ )
   {
      if ( pBufOld[i] != CHAR_CR )
         pBufNew[j++] = pBufOld[i];
   }

  /*
   * Append null char.
   */
   pBufNew[sizeNew] = '\0';

   *ppBufNew = pBufNew;
   *pSizeNew = sizeNew;
   return FUNC_OK;
}

